/**
 * @fileoverview 项目依赖安装模块
 * 负责处理项目依赖的安装，包括基础依赖和可选的代码规范工具
 */

import { default as chalk } from "chalk";
import { spawnSync } from "child_process";
import { createSources } from "./skeleton.js";
import { default as ora } from "ora";
import { errorHandler, ErrorType } from "./error-handler.js";

// 基础开发依赖列表
const requirements = [
  "webpack",
  "webpack-cli",
  "acorn",
  "terser-webpack-plugin",
  "typescript",
  "ts-loader",
  "dotenv-webpack",
  "@types/dotenv-webpack",
];

// 需要全局安装的依赖
const globalRequirements = ["typescript"];

/**
 * 检查Git是否可用
 * @param {Object} spawnOptions - 执行选项
 * @returns {boolean} Git是否可用
 */
function isGitAvailable(spawnOptions) {
  try {
    const gitCheck = spawnSync("git", ["--version"], {
      ...spawnOptions,
      stdio: "pipe",
    });
    return gitCheck.status === 0;
  } catch (error) {
    console.warn(chalk.yellow("[警告] [Git检查] 检查Git可用性时出错"));
    return false;
  }
}

/**
 * 执行npm命令的工具函数
 * @param {string} command - npm命令
 * @param {string[]} args - 命令参数
 * @param {Object} options - 执行选项
 * @returns {Object} 执行结果
 * @throws {Error} 当命令执行失败时抛出错误
 */
function executeNpmCommand(command, args, options) {
  try {
    const result = spawnSync(command, args, options);
    if (result.error) {
      throw new Error(`执行命令失败: ${result.error.message}`);
    }
    if (result.status !== 0) {
      throw new Error(`命令执行失败，退出码: ${result.status}`);
    }
    return result;
  } catch (error) {
    throw error;
  }
}

/**
 * 配置package.json的npm脚本
 * @param {Object} spawnOptions - 执行选项
 */
function configurePackageScripts(spawnOptions) {
  const pkgSetCmd = "npm";
  const pkgSetArgs = [
    "pkg",
    "set",
    'scripts.eslint:fix="eslint --fix"',
    'scripts.prettier:write="prettier . --write"',
    'scripts.prettier:check="prettier . --check"',
  ];

  try {
    const pkgSet = spawnSync(pkgSetCmd, pkgSetArgs, spawnOptions);
    if (pkgSet.error || pkgSet.status !== 0) {
      throw new Error("修改失败 package.json");
    }
  } catch (error) {
    errorHandler.handleError(
      error,
      ErrorType.CONFIGURATION,
      "配置package.json脚本"
    );
  }
}

/**
 * 安装项目依赖
 * @param {string} cwd - 当前工作目录，用于执行npm命令的目录
 * @param {Object} answers - 包含用户选择的配置答案
 * @throws {Error} 当安装过程中出现错误时抛出异常
 */
async function install(cwd, answers) {
  const spawnOptions = {
    stdio: "inherit",
    shell: true,
    cwd: cwd || process.cwd(),
  };

  const isPackage = answers.npmPackage.value === "神岛组件库";

  // 创建package.json
  console.log(chalk.blue("[信息] [初始化] 开始创建package.json"));
  console.log(
    `${chalk.cyanBright("创建 package.json...")}

> ${chalk.dim(`npm init ${isPackage ? "" : "-y"}`)}
`
  );

  if (isPackage) {
    console.log(
      chalk.yellowBright(
        "你已选择当前项目为npm包，请填写 package.json 相关信息。\n注意：入口文件[entryPoint]无需修改，脚手架会自动生成。"
      )
    );
  }

  try {
    executeNpmCommand(
      "npm",
      [
        "--registry=https://registry.npmmirror.com",
        "init",
        isPackage ? "" : "-y",
      ],
      spawnOptions
    );
    console.log(chalk.greenBright("✓ package.json 创建成功"));
    console.log(chalk.blue("[信息] [初始化] package.json 创建成功"));
  } catch (error) {
    await errorHandler.handleError(
      error,
      ErrorType.CONFIGURATION,
      "创建 package.json",
      () => {
        console.error(
          chalk.redBright("创建 package.json 失败:"),
          error.message
        );
      }
    );
    throw error;
  }

  // 配置npm包的入口文件
  if (isPackage) {
    console.log(chalk.blue("[信息] [配置] 开始配置npm包入口文件"));
    const pkgSetArgs = ["pkg", "set"];
    const isClient = answers.isServer.value === "客户端";

    pkgSetArgs.push(
      `files[0]=${isClient ? "client" : "server"}/dist/${
        isClient ? "client" : "server"
      }/src/**/*`,
      `main=${isClient ? "client" : "server"}/dist/${
        isClient ? "client" : "server"
      }/src/${isClient ? "clientApp" : "App"}.js`,
      `types=${isClient ? "client" : "server"}/dist/${
        isClient ? "client" : "server"
      }/src/${isClient ? "clientApp" : "App"}.d.ts`
    );

    try {
      const pkgSet = spawnSync("npm", pkgSetArgs, spawnOptions);
      if (pkgSet.error || pkgSet.status !== 0) {
        throw new Error("修改失败 package.json");
      }
      console.log(chalk.blue("[信息] [配置] npm包入口文件配置成功"));
    } catch (error) {
      await errorHandler.handleError(
        error,
        ErrorType.CONFIGURATION,
        "配置npm包入口文件",
        () => {
          console.error(chalk.redBright("修改失败 package.json"));
        }
      );
      process.exit(1);
    }
  }

  // 根据是否i18n选择额外的源码数组
  if (answers.i18n.value === "配置") {
    console.log(chalk.blue("[信息] [i18n] 开始配置i18n"));
    requirements.push("i18next");
  }

  // 配置代码规范工具
  if (answers.prettier.value === "配置") {
    console.log(chalk.blue("[信息] [代码质量] 开始配置代码规范工具"));
    const gitAvailable = isGitAvailable(spawnOptions);

    if (gitAvailable) {
      requirements.push(
        "eslint",
        "typescript-eslint",
        "prettier",
        "husky",
        "lint-staged"
      );
      console.log(
        chalk.cyanBright(
          "\n你已选择配置ESLint + Prettier + husky + lint-staged包，正在配置各项数据及初始化Git仓库中...\n"
        )
      );

      try {
        const gitInit = spawnSync("git", ["init"], spawnOptions);
        if (gitInit.error || gitInit.status !== 0) {
          throw new Error("Git初始化失败");
        }
        console.log(chalk.blue("[信息] [Git] Git仓库初始化成功"));
      } catch (error) {
        await errorHandler.handleError(
          error,
          ErrorType.CONFIGURATION,
          "初始化Git仓库",
          () => {
            console.error(chalk.redBright("Git初始化失败"));
          }
        );
        process.exit(1);
      }

      configurePackageScripts(spawnOptions);

      try {
        const husky = spawnSync("npx", ["husky", "init"], spawnOptions);
        if (husky.error || husky.status !== 0) {
          throw new Error("husky运行失败");
        }
        console.log(chalk.blue("[信息] [Git Hooks] husky初始化成功"));
      } catch (error) {
        await errorHandler.handleError(
          error,
          ErrorType.CONFIGURATION,
          "初始化husky",
          () => {
            console.error(chalk.redBright("husky运行失败"));
          }
        );
        process.exit(1);
      }

      try {
        await createSources(
          cwd,
          [
            {
              type: "file",
              path: "./.husky/pre-commit",
              src: "src/assets/code_quality/husky/pre-commit",
            },
            {
              type: "file",
              path: "./eslint.config.mjs",
              src: "src/assets/code_quality/eslint/eslint.config.mjs",
            },
            {
              type: "file",
              path: "./.lintstagedrc",
              src: "src/assets/code_quality/lintstaged/.lintstagedrc",
            },
            {
              type: "file",
              path: "./.prettierrc",
              src: "src/assets/code_quality/prettier/.prettierrc",
            },
            {
              type: "file",
              path: "./.prettierignore",
              src: "src/assets/code_quality/prettier/.prettierignore",
            },
          ],
          false
        );
        console.log(chalk.greenBright("Git仓库及各项数据配置成功！"));
        console.log(chalk.blue("[信息] [代码质量] 代码质量配置文件创建成功"));
      } catch (error) {
        await errorHandler.handleError(
          error,
          ErrorType.FILE_SYSTEM,
          "创建代码质量配置文件"
        );
      }
    } else {
      console.log(
        chalk.yellowBright(
          "\nGit命令不可用，将跳过husky和lint-staged的安装，仅安装ESLint和Prettier。"
        )
      );
      console.warn(chalk.yellow("[警告] [Git] Git命令不可用，跳过Git相关配置"));
      requirements.push("eslint", "typescript-eslint", "prettier");

      configurePackageScripts(spawnOptions);

      try {
        await createSources(
          cwd,
          [
            {
              type: "file",
              path: "./eslint.config.mjs",
              src: "src/assets/code_quality/eslint/eslint.config.mjs",
            },
            {
              type: "file",
              path: "./.prettierrc",
              src: "src/assets/code_quality/prettier/.prettierrc",
            },
            {
              type: "file",
              path: "./.prettierignore",
              src: "src/assets/code_quality/prettier/.prettierignore",
            },
          ],
          false
        );
        console.log(chalk.greenBright("ESLint和Prettier配置成功！"));
        console.log(chalk.blue("[信息] [代码质量] ESLint和Prettier配置成功"));
      } catch (error) {
        await errorHandler.handleError(
          error,
          ErrorType.FILE_SYSTEM,
          "创建ESLint和Prettier配置文件"
        );
      }
    }
  }

  // 安装依赖
  console.log(chalk.cyanBright("坐和放宽，正在安装依赖项中..."));
  console.log(
    chalk.blue(
      "[信息] [依赖安装] 开始安装项目依赖，使用" + answers.dependentManner.value
    )
  );

  // 使用 import 动态导入 ora
  const spinner = ora("安装项目必备的依赖中，请稍等...\n").start();

  let retryCount = 0;
  while (retryCount < 3) {
    try {
      const proc = spawnSync(
        answers.dependentManner.value,
        [
          "--registry=https://registry.npmmirror.com",
          answers.dependentManner.value === "npm" ? "--save-dev" : "-D",
          answers.dependentManner.value === "npm" ? "install" : "add",
          ...requirements,
        ],
        { ...spawnOptions, stdio: "inherit" }
      );

      if (!proc.error && proc.status === 0) {
        spinner.succeed("依赖项安装成功！");
        console.log(chalk.blue("[信息] [依赖安装] 依赖项安装成功"));
        return;
      }

      throw new Error(proc.error || `安装失败，退出码: ${proc.status}`);
    } catch (error) {
      retryCount++;
      if (retryCount === 3) {
        spinner.fail(`安装依赖项失败，已重试${retryCount}次`);
        await errorHandler.handleError(
          error,
          ErrorType.DEPENDENCY,
          "安装项目依赖",
          () => {
            console.error(
              chalk.redBright(`安装依赖项失败，已重试${retryCount}次`)
            );
          }
        );
        process.exit(1);
      }
      spinner.text = `安装失败，正在进行第${retryCount}次重试...`;
      console.warn(
        chalk.yellow(
          `[警告] [依赖安装] 依赖安装失败，准备第${retryCount}次重试`
        )
      );
      await new Promise((resolve) => setTimeout(resolve, 3000));
    }
  }
}

export { install, requirements, globalRequirements };
